<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div id="app">
    <h1>{{title}}</h1>
    <h2>{{name}}</h2>
    <h3>{{content}}</h3>
</div>
</body>
<script type="text/javascript">


    //对所有属性都要蒋婷,递归遍历所有属性
    function defineReactive(data,key,val) {
        observe(val);
        var dep = new Dep();
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            get: function() {
                if(Dep.target) {   //判断是否需要添加订阅者
                    dep.addSub(Dep.target);
                }
                return val;
            },
            set: function(newVal) {
                if (val === newVal) {
                    return;
                }
                val = newVal;
                console.log('属性' + key + '已经被监听了，现在值为：“' + newVal.toString() + '”');
                dep.notify(); // 如果数据变化，通知所有订阅者
            },

        });
    }

    function observe(data) {
        if(!data || typeof data !== 'object') {
            return;
        }
        Object.keys(data).forEach(function(key){
            defineReactive(data,key,data[key]);
        });
    }

    function Dep() {
        this.subs = [];
    }
    Dep.prototype = {
        addSub:function(sub) {
            this.subs.push(sub);
        },
        notify:function() {
            this.subs.forEach(function(sub) {
                sub.update();        //通知每个订阅者检查更新
            })
        },

    }
    Dep.target = null;

    function Watcher(vm,exp,cb) {
        this.vm = vm;    //指向SelfVue的作用域
        this.exp = exp;  //绑定属性的key值
        this.cb = cb;    //闭包
        this.value = this.get();
    }

    Watcher.prototype = {
        update:function() {
            this.run();
        },
        run:function() {
            var value = this.vm.data[this.exp];
            var oldVal = this.value;
            if(value !== oldVal) {
                this.value = value;
                this.cb.call(this.vm,value,oldVal);
            }
        },
        get:function() {
            Dep.target = this;                   // 缓存自己
            var value = this.vm.data[this.exp];  // 强制执行监听器里的get函数
            Dep.target = null;                   // 释放自己
            return value;
        },

    }

    //将Observer和Watcher关联起来
    function SelfVue(options) {
        var self = this;
        this.vm = this;
        this.data = options.data;
        Object.keys(this.data).forEach(function(key) {
            self.proxyKeys(key);     //绑定代理属性
        });
        observe(options.data);
        new Compile(options.el,this.vm);

        return this;
    }


    SelfVue.prototype = {
        proxyKeys:function(key) {
            var self = this;
            Object.defineProperty(this,key,{
                enumerable:false,
                configurable:true,
                get:function proxyGetter() {
                    return self.data[key];
                },
                set:function proxySetter(newVal) {
                    self.data[key] = newVal;
                }
            });
        },
        nodeToFragment:function(el) {
            var fragment = document.createDocumentFragment();   //createdocumentfragment()方法创建了一虚拟的节点对象，节点对象包含所有属性和方法。
            var child = el.firstChild;
            while(child) {
                // 将Dom元素移入fragment中
                fragment.appendChild(child);
                child = el.firstChild;
            }
            return fragment;
        },
        //遍历各个节点,对含有相关指定的节点进行特殊处理
        compileElement:function(el) {
            var childNodes = el.childNodes;   //childNodes属性返回节点的子节点集合，以 NodeList 对象。
            var self = this;
            //slice() 方法可从已有的数组中返回选定的元素。
            [].slice.call(childNodes).forEach(function(node) {
                var reg = /\{\{(.*)\}\}/;
                var text = node.textContent;  //textContent 属性设置或返回指定节点的文本内容
                if(self.isTextNode(node) && reg.test(text)) {      //判断是否符合{{}}的指令
                    //exec() 方法用于检索字符串中的正则表达式的匹配。
                    //返回一个数组，其中存放匹配的结果。如果未找到匹配，则返回值为 null。
                    self.compileText(node,reg.exec(text)[1]);
                }
                if(node.childNodes && node.childNodes.length) {
                    self.compileElement(node);    //继续递归遍历子节点
                }
            });
        },
        compileText:function(node,exp) {
            var self = this;
            var initText = this.vm[exp];
            this.updateText(node,initText);    // 将初始化的数据初始化到视图中
            new Watcher(this.vm,exp,function(value) {
                self.updateText(node,value);
            });

        },
        updateText:function(node,value) {
            node.textContent = typeof value == 'undefined' ? '': value;
        },

    }



    var selfVue = new SelfVue({
        el:'#app',
        data:{
            title:'aaa',
            name:'bbb',
            content:'ccc'
        }
    });
    window.setTimeout(function() {
        selfVue.title = 'ddd';
        selfVue.name = 'eee';
        selfVue.content = 'fff'
    },2000);
</script>
</html>